use std::sync::Arc;

use aspect_core::message_queue::MessageQueueId;
use futures::future::TryFutureExt;
use jsonrpsee::types::{ErrorCode, ErrorObject};
use tokio::sync::{oneshot, Mutex};
use tracing::*;

use aspect_homeserver_rpc::*;

use crate::constants::*;
use crate::storage::HomeserverStorage;

pub struct RpcImpl {
    storage: Arc<HomeserverStorage>,
    stop_sig: Mutex<Option<oneshot::Sender<()>>>,
}

impl RpcImpl {
    pub fn new(storage: Arc<HomeserverStorage>, stop_sig: oneshot::Sender<()>) -> Self {
        Self {
            storage,
            stop_sig: Mutex::new(Some(stop_sig)),
        }
    }

    async fn queue_device_msg<'c>(
        &self,
        mqid: &MessageQueueId,
        msg: &[u8],
    ) -> Result<u64, ErrorObject<'c>> {
        // TODO better errors
        let idx = self
            .storage
            .queue_device_msg(mqid, msg)
            .map_err(|e| {
                warn!(err = %e, "failed to queue message");
                ErrorObject::from(ErrorCode::InternalError)
            })
            .await?;
        Ok(idx)
    }
}

#[async_trait]
impl HomeserverRpcServer for RpcImpl {
    async fn status(&self) -> RpcResult<StatusReport> {
        Ok(StatusReport { healthy: true })
    }

    async fn stop(&self) -> RpcResult<()> {
        let mut sig = self.stop_sig.lock().await;
        if let Some(sig) = sig.take() {
            sig.send(()).expect("rpc/stop: send stop sig");
        }

        Ok(())
    }

    async fn queue_server_msg(&self, msg: String) -> RpcResult<u64> {
        let mqid = MessageQueueId::Server(DMQID_SERVER_NOTIFICATIONS.to_string());
        Ok(self.queue_device_msg(&mqid, msg.as_bytes()).await?)
    }
}
